#include "BTreeNode.h"

using namespace std;

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::read(PageId pid, const PageFile& pf)
{
	char buffer[PageFile::PAGE_SIZE];
	pf.read(pid, buffer);
	return loadCharArray(buffer);
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::write(PageId pid, PageFile& pf)
{
	pf.write(pid, toCharArray());
	return 0;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTLeafNode::getKeyCount()
{
	return keyCount;
}

/*
 * Insert a (key, rid) pair to the node.
 * @param key[IN] the key to insert
 * @param rid[IN] the RecordId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTLeafNode::insert(int key, const RecordId& rid)
{
	int index = 0;
	while (index < keyCount && key > keyid[index])
		index++;
	if (keyCount == 0)
	{
		keyid[0] = key;
		recordid[0] = rid;
	}
	else
	{
		if (index != keyCount)
		{
			for (int i=keyCount; i>=index; i--)
			{
				keyid[i] = keyid[i-1];
				recordid[index] = recordid[index-1];
			}
		}
		keyid[index] = key;
		recordid[index] = rid;
	}
	keyCount++;
	return 0;
}

/*
 * Insert the (key, rid) pair to the node
 * and split the node half and half with sibling.
 * The first key of the sibling node is returned in siblingKey.
 * @param key[IN] the key to insert.
 * @param rid[IN] the RecordId to insert.
 * @param sibling[IN] the sibling node to split with. This node MUST be EMPTY when this function is called.
 * @param siblingKey[OUT] the first key in the sibling node after split.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::insertAndSplit(int key, const RecordId& rid, 
                              BTLeafNode& sibling, int& siblingKey)
{
	//middle point is at first sibling
	insert(key, rid);
	int mid = keyCount / 2 + 1;
	siblingKey = keyid[mid];

	sibling.keyCount = keyCount - mid + 1;
	sibling.fatherpid = fatherpid;
	sibling.nextpid = nextpid;
	for (int i=0; i<sibling.keyCount; i++)
	{
		sibling.keyid[i] = keyid[i + mid];
		sibling.recordid[i] = recordid[i + mid];
	}

	for (int i=mid; i<keyCount; i++)
	{
		keyid[i] = 0;
		recordid[i].pid = 0;
		recordid[i].sid = 0;
	}
	keyCount = mid;

	return 0;
}

/*
 * Find the entry whose key value is larger than or equal to searchKey
 * and output the eid (entry number) whose key value >= searchKey.
 * Remeber that all keys inside a B+tree node should be kept sorted.
 * @param searchKey[IN] the key to search for
 * @param eid[OUT] the entry number that contains a key larger than or equalty to searchKey
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::locate(int searchKey, int& eid)
{
	for (int i=0; i<keyCount; i++)
		if (keyid[i] >= searchKey)
		{
			eid = i;
			return 0;
		}
	eid = -1;
	return 0;
}

/*
 * Read the (key, rid) pair from the eid entry.
 * @param eid[IN] the entry number to read the (key, rid) pair from
 * @param key[OUT] the key from the entry
 * @param rid[OUT] the RecordId from the entry
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::readEntry(int eid, int& key, RecordId& rid)
{
	if (eid < 0)
		return -1;
	else
	{
		key = keyid[eid];
		rid = recordid[eid];
	}
	return 0;
}

/*
 * Return the pid of the next slibling node.
 * @return the PageId of the next sibling node 
 */
PageId BTLeafNode::getNextNodePtr()
{
	return nextpid;
}

/*
 * Set the pid of the next slibling node.
 * @param pid[IN] the PageId of the next sibling node 
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTLeafNode::setNextNodePtr(PageId pid)
{
	nextpid = pid;
	return 0;
}

char* BTLeafNode::toCharArray()
{
	char buffer[PageFile::PAGE_SIZE];
	memset(buffer, 0, sizeof(buffer));
	memcpy(buffer, this, sizeof(BTLeafNode));
	return buffer;
}

int BTLeafNode::loadCharArray(char* buffer)
{
	memcpy(this, buffer, sizeof(BTLeafNode));
	return 0;
}

/*
 * Read the content of the node from the page pid in the PageFile pf.
 * @param pid[IN] the PageId to read
 * @param pf[IN] PageFile to read from
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::read(PageId pid, const PageFile& pf)
{
	char buffer[PageFile::PAGE_SIZE];
	pf.read(pid, buffer);
	return loadCharArray(buffer);
}
    
/*
 * Write the content of the node to the page pid in the PageFile pf.
 * @param pid[IN] the PageId to write to
 * @param pf[IN] PageFile to write to
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::write(PageId pid, PageFile& pf)
{
	pf.write(pid, toCharArray());
	return 0;
}

/*
 * Return the number of keys stored in the node.
 * @return the number of keys in the node
 */
int BTNonLeafNode::getKeyCount()
{
	return keyCount;
}


/*
 * Insert a (key, pid) pair to the node.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @return 0 if successful. Return an error code if the node is full.
 */
RC BTNonLeafNode::insert(int key, PageId pid)
{
	int index = 0;
	while (index < keyCount && key > keyid[index])
		index++;
	if (keyCount == 0)
	{
		keyid[0] = key;
		pageid[0] = pid;
	}
	else
	{
		if (index != keyCount)
		{
			for (int i=keyCount; i>=index; i--)
			{
				keyid[i] = keyid[i-1];
				pageid[index] = pageid[index-1];
			}
		}
		keyid[index] = key;
		pageid[index] = pid;
	}
	keyCount++;
	return 0;
}

/*
 * Insert the (key, pid) pair to the node
 * and split the node half and half with sibling.
 * The middle key after the split is returned in midKey.
 * @param key[IN] the key to insert
 * @param pid[IN] the PageId to insert
 * @param sibling[IN] the sibling node to split with. This node MUST be empty when this function is called.
 * @param midKey[OUT] the key in the middle after the split. This key should be inserted to the parent node.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::insertAndSplit(int key, PageId pid, BTNonLeafNode& sibling, int& midKey)
{
	insert(key, pid);
	int mid = (keyCount + 1) / 2;
	midKey = keyid[mid];

	sibling.keyCount = keyCount - mid;
	sibling.fatherpid = fatherpid;
	for (int i=0; i<sibling.keyCount; i++)
	{
		sibling.keyid[i] = keyid[i + mid + 1];
		sibling.pageid[i] = pageid[i + mid + 1];
	}
	sibling.pageid[sibling.keyCount] = pageid[keyCount];

	for (int i=mid; i<=keyCount; i++)
	{
		keyid[i] = 0;
		pageid[i] = 0;
	}
	keyCount = mid;
	return 0;
}

/*
 * Given the searchKey, find the child-node pointer to follow and
 * output it in pid.
 * @param searchKey[IN] the searchKey that is being looked up.
 * @param pid[OUT] the pointer to the child node to follow.
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::locateChildPtr(int searchKey, PageId& pid)
{
	for (int i=0; i<keyCount; i++)
		if (keyid[i] > searchKey)
		{
			pid = pageid[i];
			return 0;
		}
	pid = pageid[keyCount];
	return 0;
}

/*
 * Initialize the root node with (pid1, key, pid2).
 * @param pid1[IN] the first PageId to insert
 * @param key[IN] the key that should be inserted between the two PageIds
 * @param pid2[IN] the PageId to insert behind the key
 * @return 0 if successful. Return an error code if there is an error.
 */
RC BTNonLeafNode::initializeRoot(PageId pid1, int key, PageId pid2)
{
	keyid[0] = key;
	pageid[0] = pid1;
	pageid[1] = pid2;
	keyCount = 1;
	return 0;
}

char* BTNonLeafNode::toCharArray()
{
	char buffer[PageFile::PAGE_SIZE];
	memset(buffer, 0, sizeof(buffer));
	memcpy(buffer, this, sizeof(BTNonLeafNode));
	return buffer;
}

int BTNonLeafNode::loadCharArray(char* buffer)
{
	memcpy(this, buffer, sizeof(BTNonLeafNode));
	return 0;
}
